#include "TobiiDevice.h"

TobiiDevice::TobiiDevice(IODeviceContext* context)
	: TobiiDeviceBase(context)
{
	//Init();
}


TobiiDevice::~TobiiDevice()
{
	//Deinit();
}


int TobiiDevice::Init()
{
	m_hContext = TX_EMPTY_HANDLE;
	m_hConnectionStateChangedTicket = TX_INVALID_TICKET;
	m_hEventHandlerTicket = TX_INVALID_TICKET;
	bool success;

	// initialize and enable the context that is our link to the EyeX Engine.
	success = txInitializeEyeX(TX_EYEXCOMPONENTOVERRIDEFLAG_NONE, NULL, NULL, NULL, NULL) == TX_RESULT_OK;
	success &= txCreateContext(&m_hContext, TX_FALSE) == TX_RESULT_OK;
	success &= InitializeGlobalInteractorSnapshot(m_hContext);
	success &= RegisterConnectionStateChangedHandler() == true;
	success &= RegisterEventHandler() == true;
	success &= txEnableConnection(m_hContext) == TX_RESULT_OK;

	return success;
}

int TobiiDevice::DeInit()
{
	bool success;
	txDisableConnection(m_hContext);
	txReleaseObject(&m_hGlobalInteractorSnapshot);
	success = txShutdownContext(m_hContext, TX_CLEANUPTIMEOUT_DEFAULT, TX_FALSE) == TX_RESULT_OK;
	success &= txReleaseContext(&m_hContext) == TX_RESULT_OK;
	success &= txUninitializeEyeX() == TX_RESULT_OK;

	if (success) return 1;
	else return -1;
}

void TX_CALLCONVENTION TobiiDevice::OnEngineConnectionStateChanged(TX_CONNECTIONSTATE connectionState, TX_USERPARAM userParam)
{
	switch (connectionState) {
	case TX_CONNECTIONSTATE_CONNECTED: {
		bool success;
		printf("The connection state is now CONNECTED (We are connected to the EyeX Engine)\n");
		// commit the snapshot with the global interactor as soon as the connection to the engine is established.
		// (it cannot be done earlier because committing means "send to the engine".)
		success = txCommitSnapshotAsync(m_hGlobalInteractorSnapshot, NULL, NULL) == TX_RESULT_OK;
		if (!success) {
			printf("Failed to initialize the data stream.\n");
		}
		else {
			printf("Waiting for gaze data to start streaming...\n");
		}
	}
	break;

	case TX_CONNECTIONSTATE_DISCONNECTED:
		printf("The connection state is now DISCONNECTED (We are disconnected from the EyeX Engine)\n");
		break;

	case TX_CONNECTIONSTATE_TRYINGTOCONNECT:
		printf("The connection state is now TRYINGTOCONNECT (We are trying to connect to the EyeX Engine)\n");
		break;

	case TX_CONNECTIONSTATE_SERVERVERSIONTOOLOW:
		printf("The connection state is now SERVER_VERSION_TOO_LOW: this application requires a more recent version of the EyeX Engine to run.\n");
		break;

	case TX_CONNECTIONSTATE_SERVERVERSIONTOOHIGH:
		printf("The connection state is now SERVER_VERSION_TOO_HIGH: this application requires an older version of the EyeX Engine to run.\n");
		break;
	}
}

void TX_CALLCONVENTION TobiiDevice::OnSnapshotCommitted(TX_CONSTHANDLE hAsyncData, TX_USERPARAM param)
{

}

void TobiiDevice::OnGazeDataEvent(TX_HANDLE hGazeDataBehavior)
{
	TX_GAZEPOINTDATAEVENTPARAMS eventParams;
	if (txGetGazePointDataEventParams(hGazeDataBehavior, &eventParams) == TX_RESULT_OK) {
		// printf("Gaze Data: (%.1f, %.1f) timestamp %.0f ms\n", eventParams.X, eventParams.Y, eventParams.Timestamp);
		TobbiGazePoint* pGazePoint = new TobbiGazePoint;
		pGazePoint->x = (int)eventParams.X;
		pGazePoint->y = (int)eventParams.Y;
		Notice(GetType(), IO_OPERATION_TOBII_GET_GAZE_POINT, reinterpret_cast<unsigned int>(pGazePoint));
	}
	else {
		// printf("Failed to interpret gaze data event packet.\n");
	}
}

void TX_CALLCONVENTION TobiiDevice::OnEvent(TX_CONSTHANDLE hAsyncData, TX_USERPARAM userParam)
{
	TX_HANDLE hEvent = TX_EMPTY_HANDLE;
	TX_HANDLE hBehavior = TX_EMPTY_HANDLE;

	txGetAsyncDataContent(hAsyncData, &hEvent);

	// NOTE. Uncomment the following line of code to view the event object. The same function can be used with any interaction object.
	//OutputDebugStringA(txDebugObject(hEvent));

	if (txGetEventBehavior(hEvent, &hBehavior, TX_BEHAVIORTYPE_GAZEPOINTDATA) == TX_RESULT_OK) {
		OnGazeDataEvent(hBehavior);
		txReleaseObject(&hBehavior);
	}

	// NOTE since this is a very simple application with a single interactor and a single data stream, 
	// our event handling code can be very simple too. A more complex application would typically have to 
	// check for multiple behaviors and route events based on interactor IDs.

	txReleaseObject(&hEvent);
}

bool TobiiDevice::InitializeGlobalInteractorSnapshot(TX_CONTEXTHANDLE hContext)
{
	TX_HANDLE hInteractor = TX_EMPTY_HANDLE;
	TX_GAZEPOINTDATAPARAMS params = { TX_GAZEPOINTDATAMODE_LIGHTLYFILTERED };
	bool success;

	success = txCreateGlobalInteractorSnapshot(
		hContext,
		"Twilight Sparkle",
		&m_hGlobalInteractorSnapshot,
		&hInteractor) == TX_RESULT_OK;
	success &= txCreateGazePointDataBehavior(hInteractor, &params) == TX_RESULT_OK;

	txReleaseObject(&hInteractor);

	return success;
}

void TobiiDevice::SendOperation(IODeviceOperation operation, unsigned int param)
{

}
